import store from '../store'
import getEnv from './env'
import useMessage from './message'

type Params = Record<string, string | number>

type Method = 'GET' | 'POST' | 'PUT' | 'DELETE'

type RequestType = 'json' | 'blob'

type SuccessFn = (response: Response) => void

type ErrorFn = (error: Error) => void

interface RequestOptions {
  api?: string
  baseURL?: string
  params?: Params
  timeout?: number
  headers?: Record<string, string>
  url: string
  method: Method
  data?: unknown
  requestType?: RequestType
}

interface CtmResponse<T> {
  code?: number
  data?: T | null
  msg?: string
  error?: unknown
}

function setParams(params: Params | undefined) {
  let url = ''
  if (!params) return url
  Object.keys(params).forEach((key, index) => {
    if (typeof params[key] === 'string' || typeof params[key] === 'number') {
      url += `${index ? '&' : '?'}${key}=${params[key]}`
    }
  })
  return url
}

class Request {
  public api: string
  public baseURL: string
  public params: Params | undefined
  public headers: HeadersInit | undefined
  public timeout: number
  public url: string
  public method: Method
  public data: unknown

  constructor(ro: RequestOptions) {
    // 设置默认api
    this.api = ro.api!
    // 设置默认baseURL
    this.baseURL = ro.baseURL!
    // 设置默认params
    this.params = ro.params
    // 设置默认headers
    this.headers = ro.headers
    // 设置默认timeout
    this.timeout = ro.timeout!
    // 设置默认url
    this.url = ro.url
    // 设置默认method
    this.method = ro.method
    // 设置默认data
    this.data = ro.data
  }

  async create(successFn: SuccessFn, errorFn: ErrorFn) {
    const controll = new AbortController()
    const timer = setTimeout(() => {
      if (!this.timeout) return
      controll.abort()
    }, this.timeout)

    try {
      const response = await fetch(this.api + this.baseURL + this.url + setParams(this.params), {
        method: this.method,
        headers: this.headers as HeadersInit,
        body: this.data as BodyInit,
        signal: controll.signal
      })
      if (response.ok) {
        successFn(response)
      } else {
        errorFn(new Error(response.statusText))
      }
    } catch (e) {
      errorFn(e as Error)
    } finally {
      clearInterval(timer)
    }
  }
}

async function create<T>(requestInfo: RequestOptions): Promise<CtmResponse<T>> {
  const message = useMessage()
  //设置默认值
  const ro = Object.assign(
    {
      api: getEnv('api'),
      baseURL: getEnv('baseURL'),
      timeout: getEnv('timeout'),
      headers: {},
      requestType: 'json'
    },
    requestInfo
  )

  // 如果是json请求，需要转换数据格式，并设置请求头
  if (ro.requestType === 'json') {
    ro.data = JSON.stringify(ro.data)
    ro.headers!['Content-Type'] = 'application/json'
  }

  // 设置token
  ro.headers!['Authorization'] = store.getState().token

  return new Promise((resolve, reject) => {
    new Request(ro).create(
      async (response: Response) => {
        const json = (await response.json()) as CtmResponse<T>
        if (json.code !== 200) {
          // 错误处理
          message.error(json.msg)
          reject(json)
        } else {
          // 正常返回
          resolve(json)
        }
      },
      (e) => {
        if (e.name === 'AbortError') {
          message.error('请求超时')
        } else {
          message.error(e.message)
        }
        reject({
          code: 500,
          data: null,
          msg: e.message
        })
      }
    )
  })
}

export default create
